home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 2 / Gold Medal Software Volume 2 (Gold Medal) (1994).iso / prog / asm_0_m.arj / JDOS.ASM < prev    next >
Assembly Source File  |  1989-03-18  |  31KB  |  1,027 lines

  1. TITLE  JDOS.ASM
  2. ;
  3. COMMENT *
  4.  
  5.     Purpose:    Perform 'DOS' functions from within most programs.
  6.     Allows other programs to be executed then return to the interrupted
  7.     program.
  8.  
  9.     Created:    11-MAR-1989        Richard B. Johnson
  10.  
  11.     Modified:
  12.     12-MAR-1989    V1.01            Richard B. Johnson
  13.     Added sign-on screens using direct screen-writes.
  14.     Added DOS version check. DOS V2.## does not work correctly.
  15.  
  16.     13-MAR-1989    V1.02            Richard B. Johnson
  17.     Added code to intercept interrupt vectors that the interrupted
  18.     program might be using. If the interrupted program was using
  19.     the timer-tick, the program would crash.
  20.  
  21.     Changed reserved word "WIDTH" to label "@WIDTH". Would produce a
  22.     warning message under MASM but not under OPTASM.
  23.  
  24.     14-MAR-1989    V1.03            Richard B. Johnson
  25.     Added code to save the default interrupt table during initialization
  26.     and use this default table during the spawned process. This should
  27.     prevent possible reentry problems if spawned programs spawn other
  28.     processes also.
  29.  
  30.     14-MAR-1989    V1.04            Richard B. Johnson
  31.     Added code to save the state of four possible 8250 UARTS including
  32.     any enabled interrupts and the baud-rate. The UART status is restored
  33.     before returning to the interrupted program. This allows communi-
  34.     cations programs to be interrupted, other communications programs
  35.     executed with different baud-rates, etc., then the return to the
  36.     interrupted program without any errors.
  37.  
  38.     14-MAR-1989    V1.05            Richard B. Johnson
  39.     Added code to mask off the interrupt controller immediately
  40.     upon return from the spawmed process. Some communications programs
  41.     were leaving "hot" interrupts when they exited!! Since not even the
  42.     stack is in a known place when the EXEC call returns, this could
  43.     cause a crash. Symptom = "divide by zero error".
  44.  
  45.     16-MAR-1989    V1.06            Richard B. Johnson
  46.     Added code to search for the screen segment to accommodate COMPAQ
  47.     "compatibles" that ALWAYS have the screen segment at B800H without
  48.     regard for any standards.
  49.  
  50.     16-MAR-1989    V1.07            Richard B. Johnson
  51.     Fixed bug in code that failed to restore the interrupt controller
  52.     mask after a return from the spawned process. No crashes were
  53.     reported, but it's possible that a crash could occur if a program
  54.     being executed failed to restore "hot" interrupt vectors when
  55.     it was aborted. This might happen if the program being executed
  56.     by the spawned process aborted because of a fatal error.
  57.  
  58.     Notice:        This program must be compiled as a '.COM' file!
  59.     MASM JDOS;
  60.     LINK JDOS;
  61.     EXE2BIN JDOS.EXE JDOS.COM
  62.     DEL JDOS.EXE
  63. *
  64. VERS    STRUC
  65.     DB    'V1.07'            ; Set version number here ONLY.
  66. @VERS    DB    ' '
  67. VERS    ENDS
  68. ;
  69. TRUE    EQU    -1            ; Set logicals
  70. FALSE    EQU    NOT TRUE
  71. TESTING    EQU    FALSE            ; Debugging conditional
  72. CR    EQU    0DH
  73. LF    EQU    0AH
  74. INT_CTL    EQU    21H            ; Interrupt controller mask addr.
  75. INT_RES    EQU    20H            ; Interrupt controller reset addr.
  76. NOS_EOI    EQU    20H            ; Non-specific end-of-interrupt
  77. MS_DOS    EQU    21H            ; Operating system interrupt
  78. VIDEO    EQU    10H            ; Video BIOS code interrupr
  79. ENVIR    EQU    2CH            ; Address of environment segment
  80. KB    EQU    1024            ; One kb =
  81. MEM    EQU    32            ; Extra bytes of memory required
  82. TICK1    EQU    08H            ; Clock tick
  83. TICK2    EQU    1CH            ; User timer
  84. SCR_LIN    EQU    15            ; Line to start
  85. @WIDTH    EQU    28            ; Width of the box
  86. HEIGHT    EQU    6            ; Height of the box
  87. COL_SCR    EQU    (80 - @WIDTH) / 2     ; Column to center
  88. SCR_WRD    EQU    80            ; Words per line on the screen
  89. LOCUS    EQU    (SCR_LIN * SCR_WRD * 2) + (COL_SCR * 2)
  90. ;
  91. IN8250    STRUC
  92. LIN_CTR    DB    ?            ; Line control register
  93. DIVISOR    DW    ?            ; Divisor
  94. INT_CTR    DB    ?            ; Interrupt control register
  95. MOD_CTR    DB    ?            ; Modem control register
  96. IN8250    ENDS
  97. ;
  98. ;    Possible UART base addresses
  99. ;
  100. ADDR1    EQU    03F8H            ; COM1
  101. ADDR2    EQU    02F8H            ; COM2
  102. ADDR3    EQU    03E8H            ; COM3
  103. ADDR4    EQU    02E8H            ; COM4
  104. ;
  105. PSEG    SEGMENT    PARA PUBLIC 'CODE'
  106. START    EQU    $
  107.     ASSUME CS:PSEG, DS:PSEG, ES:PSEG, SS:NOTHING
  108.     ORG    100H
  109. MAIN    PROC    NEAR
  110.     JMP    INIT
  111. MAIN    ENDP
  112. ;
  113. ;    Write the string addressed by SI to a box on the screen.
  114. ;
  115. SIGNON    PROC    NEAR
  116.     PUSH    ES
  117.     MOV    ES,WORD PTR [SCR_SEG]    ; Pick up screen segment
  118.     MOV    DI,LOCUS        ; Where to start the box
  119.     MOV    CX,HEIGHT        ; Height of the box
  120. BOX0:    PUSH    CX            ; Save height
  121.     MOV    AH,01110000B        ; Reverse video
  122.     MOV    CX,@WIDTH        ; Width of the box
  123. BOX1:    LODSB                ; Get LOGO byte
  124.     STOSW                ; Write the word
  125.     LOOP    BOX1            ; Continue
  126.     ADD    DI,(SCR_WRD - @WIDTH ) * 2 ; For next line
  127.     POP    CX            ; Restore height
  128.     LOOP    BOX0
  129.     POP    ES            ; Restore segment
  130.     RET
  131. SIGNON    ENDP
  132. ;
  133. ;    This is the main clock interrupt entry point. We check to see if
  134. ;    [EFLAG] has been set. If not, we continue to old vector. If it has
  135. ;    been set, we check to see if the sub-process has already been spawned
  136. ;    by looking at [SPAWN]. If the process has not been spawned, we check
  137. ;    the interrupted program's CS. It must be below 640k and above our
  138. ;    CS or else a sub-process is not created.
  139. ;
  140. ENTRY    PROC    FAR
  141.     PUSH    BP
  142.     MOV    BP,SP            ; Set up index
  143.     PUSH    AX            ; Save a register
  144.     PUSH    DS            ; Save segment
  145. ;
  146.     PUSH    CS
  147.     POP    DS            ; DS = CS
  148. ;
  149.     CMP    BYTE PTR [SPAWN],0    ; See if we spawned the process
  150.     JNZ    BYPASS            ; Yes, continue
  151.     CMP    BYTE PTR [EFLAG],0    ; Check entry flag
  152.     JZ    BYPASS            ; We don't want to do it
  153. ;
  154. ;    See what we interrupted. Must be between current CS and end of
  155. ;    640k for us to enter.
  156. ;
  157.                     ; BP = Pushed BP
  158.                     ; BP + 2 = IP
  159.                     ; BP + 4 = CS
  160.                     ; BP + 6 = FLAGS
  161.     MOV    AX,CS            ; Pick up our code-segment
  162.     CMP    [BP+4],AX        ; Compare with interrupted segment
  163.     JC    BYPASS            ; Not the code to interrupt
  164.     CMP    [BP+4],9000H        ; Check high limit
  165.     JNC    BYPASS            ; Not the code to interrupt
  166. ;
  167.     MOV    BYTE PTR [SPAWN],0FFH    ; Set flag
  168.     CALL    @LOCAL            ; Call the local routine
  169.     MOV    BYTE PTR [SPAWN],0    ; Reset the flag
  170.     MOV    BYTE PTR [EFLAG],0    ; Reset rentry flag.
  171. ;
  172. BYPASS:    POP    DS            ; Restore segment
  173.     POP    AX            ; Restore register
  174.     POP    BP            ; Restore index
  175.     JMP    DWORD PTR CS:[OLD_CLK]    ; Continue
  176. ENTRY    ENDP
  177. ;
  178. ;    Keyboard interrupt extension. If the call is to check status, we
  179. ;    ignore it and simply jump to the old interrupt vector. If the call
  180. ;    is to receive a character, we make the call ourselves, them check
  181. ;    the character. If the character is the one used to spawn the sub-
  182. ;    process, we set the sub-process flag, [EFLAG]. Then we substitute
  183. ;    a null character for the key-code and return.
  184. ;
  185. LCL_KBD    PROC    FAR
  186.     CMP    BYTE PTR CS:[SPAWN],0    ; Already in the spawned process?
  187.     JZ    ASK            ; No, Check the request
  188. NOCHR:    JMP    DWORD PTR CS:[OLD_KBD]    ; Continue
  189. ASK:    CMP    AH,0            ; Do we want a character?
  190.     JNZ    NOCHR            ; No, checking status
  191.     PUSHF                ; Dummy INT
  192.     CALL    DWORD PTR CS:[OLD_KBD]    ; Go get the character
  193.     CMP    AX,2B1CH        ; Character we want?
  194.     JNZ    HOME            ; Nope
  195.     PUSH    BX            ; Yes, beep
  196.     PUSH    BP            ; Is sometimes destroyed
  197.     MOV    AX,0E07H        ; Sound the bell
  198.     MOV    BX,7            ; Normal attribute
  199.     INT    VIDEO            ; Video ROM BIOS
  200.     POP    BP            ; Restore registers
  201.     POP    BX
  202.     MOV    BYTE PTR CS:[EFLAG],0FFH ; ..and set the flag
  203.     MOV    AX,0            ; Substitute
  204. HOME:    RET    2            ; Return to caller
  205. LCL_KBD    ENDP
  206. ;
  207. ;    This is the local procedure to create a subprocess.
  208. ;
  209. @LOCAL    PROC    NEAR
  210.     MOV    AX,[BP+4]        ; BP = pushed BP
  211.                     ; BP + 2 = IP
  212.                     ; BP + 4 = CS
  213.                     ; BP + 6 = FLAGS
  214.     MOV    WORD PTR [CS_SAV],AX    ; Save the caller's code-segment
  215.     CLI                ; No interrupts
  216.     CLD                ; Forwards
  217.     MOV    WORD PTR [SS_SAV],SS    ; Save segment
  218.     MOV    WORD PTR [SP_SAV],SP    ; Save pointer
  219.     MOV    AX,CS            ; Get our segment
  220.     MOV    SS,AX            ; Into stack segment
  221.     MOV    SP,OFFSET STKTOP    ; Set up new stack
  222.     STI                ; Allow interrupts
  223.     PUSH    BX            ; Save all registers
  224.     PUSH    CX
  225.     PUSH    DX
  226.     PUSH    SI
  227.     PUSH    DI
  228.     PUSH    ES
  229.     MOV    DS,AX            ; Fix up segments
  230.     MOV    ES,AX
  231. ;
  232.     MOV    AL,NOS_EOI
  233.     OUT    INT_RES,AL        ; Reset hardware controller
  234. ;
  235.     CALL    SAV_UAR            ; Save UART parameters
  236.     CALL    SAV_DIR            ; Save the directory
  237.     CALL    SAV_DTA            ; Save data transfer area
  238.     CALL    SAV_SCR            ; Save screen data
  239.     CALL    SAV_MEM            ; Free up some memory
  240.     CALL    CLR_SCR            ; Clear the screen
  241.     CALL    SAV_INT            ; Save interrupt table
  242.  
  243.     MOV    BYTE PTR DS:[80H],0    ; Show no bytes typed
  244.     MOV    WORD PTR [SP_NEW],SP    ; Save stack
  245.     MOV    DX,OFFSET COMMAND    ; Point to COMMAND.COM
  246.     MOV    BX,OFFSET BLOCK        ; Parameter block for the load
  247.     MOV    AX,4B00H        ; Load/execute program
  248.     INT    MS_DOS            ; Doit-toit
  249.     CLI                ; Quiet while I fix
  250.     MOV    AL,10011101B        ; Mask off all but required interrupts
  251. ;                  ||||||||_________ Timer
  252. ;                  |||||||__________ Keyboard
  253. ;                  ||||||___________ Reserved
  254. ;                  |||||____________ Async (2)
  255. ;                  ||||_____________ Async (1)
  256. ;                  |||______________ Hard drive
  257. ;                  ||_______________ Floppy drive
  258. ;                  |________________ Printer
  259. ;
  260.     OUT    INT_CTL,AL        ; The interrupt controller
  261.     MOV    AX,CS            ; Restore all segments
  262.     MOV    DS,AX
  263.     MOV    ES,AX
  264.     MOV    SS,AX
  265.     MOV    SP,WORD PTR [SP_NEW]    ; Restore stack
  266.     STI                ; Allow interrupts
  267.     CLD                ; Could be messed up
  268. ;
  269.     JNC    GOOD
  270.     MOV    SI,OFFSET ERROR1    ; Point to string
  271.     CALL    PROMPT            ; Display to console
  272.     MOV    AH,0            ; Get response
  273.     PUSHF
  274.     CALL    DWORD PTR [OLD_KBD]    ; Wait for a response
  275. ;
  276. GOOD:    CALL    RES_DIR            ; Restore the directory
  277.     CALL    RES_DTA            ; Restore data transfer area
  278.     CALL    RES_MEM            ; Re-acquire the memory we used
  279.     CALL    RES_SCR            ; Restore screen
  280.     CALL    RES_INT            ; Restore interrupt table
  281.     CALL    RES_UAR            ; Restore UART parameters.
  282.     POP    ES            ; Restore all registers
  283.     POP    DI
  284.     POP    SI
  285.     POP    DX
  286.     POP    CX
  287.     POP    BX
  288.     CLI
  289.     MOV    SS,WORD PTR [SS_SAV]
  290.     MOV    SP,WORD PTR [SP_SAV]
  291.     STI
  292.     RET
  293. @LOCAL    ENDP
  294. ;
  295. ;    Free up memory. Save memory contents in file VIRTUAL.MEM.
  296. ;
  297. SAV_MEM    PROC    NEAR
  298.     MOV    DX,OFFSET VIRMEM    ; Point to code-data filename
  299.     MOV    AX,3C00H        ; Create file function
  300.     XOR    CH,CH            ; File attributes
  301.     MOV    CL,00000110B        ; Hidden/system
  302.     INT    MS_DOS            ; Create the file
  303.     JNC    W1            ; Good create
  304.     MOV    DX,OFFSET VIRMEM    ; Point to filename
  305.     MOV    SI,OFFSET ERROR5    ; Point to 'create'
  306.     JMP    SHO_ERR            ; Show the error, implied return
  307. ;
  308. W1:    MOV    BX,AX            ; File handle
  309.     PUSH    DS            ; Save segment
  310.     MOV    DS,WORD PTR [CS_SAV]    ; Pick up code segment
  311. WR_ALL:    MOV    AX,4000H        ; Write to file
  312.     MOV    CX,0FFFFH         ; 64k -1
  313.     XOR    DX,DX            ; Offset 0
  314.     INT    MS_DOS            ; Write the file
  315.     CMP    AX,CX            ; Write as requested?
  316.     JNZ    WR_OK            ; Exit, bad write
  317.     MOV    AX,DS            ; Pick up segment
  318.     ADD    AX,1000H        ; Next segment
  319.     MOV    DS,AX            ; Index 64k
  320.     CMP    AX,0A000H        ; Check limit
  321.     JC    WR_ALL            ; Continue
  322. WR_OK:    POP    DS            ; Restore segment
  323.     JNC    W2            ; Good write
  324.     MOV    DX,OFFSET VIRMEM    ; Point to filename
  325.     MOV    SI,OFFSET ERROR6    ; Point to 'write'
  326.     JMP    SHO_ERR            ; Show the error, implied return
  327. ;
  328. W2:    MOV    AX,3E00H        ; Close file handle
  329.     INT    MS_DOS
  330.     JNC    W3            ; Good close
  331.     MOV    DX,OFFSET VIRMEM    ; Point to filename
  332.     MOV    SI,OFFSET ERROR7    ; Point to 'close'
  333.     JMP    SHO_ERR            ; Show the error, implied return
  334. ;
  335. W3:    MOV    BX,(100H SHR 4)        ; Release all memory above 100H
  336.     PUSH    ES            ; Save segment
  337.     MOV    ES,WORD PTR [CS_SAV]    ; Caller's code-segment
  338.     MOV    AX,4A00H        ; Modify memory
  339.     INT    MS_DOS
  340.     POP    ES            ; Restore segment
  341.     IF    TESTING
  342.     JNC    MEM1
  343. ;
  344.     MOV    SI,OFFSET ERROR2    ; Point to memory error message.
  345.     CALL    PROMPT            ; Desplay on the screen
  346.     MOV    AH,0
  347.     PUSHF
  348.     CALL    DWORD PTR [OLD_KBD]    ; Wait for a response
  349.     ENDIF
  350. MEM1:    RET
  351. SAV_MEM    ENDP
  352. ;
  353. ;    Acquire memory. Restore it's contents from file VIRTUAL.MEM.
  354. ;
  355. RES_MEM    PROC    NEAR
  356.     MOV    BX,0FFFFH         ; Get everything back.
  357.     MOV    AX,4A00H        ; Modify memory
  358.     PUSH    ES
  359.     MOV    ES,WORD PTR [CS_SAV]    ; Pick up code segment
  360.     INT    MS_DOS
  361.     POP    ES
  362. ;
  363.     MOV    DX,OFFSET VIRMEM    ; Point to code-data filename
  364.     MOV    AX,3D00H        ; Open file for reading
  365.     XOR    CX,CX            ; File attributes
  366.     INT    MS_DOS            ; Open the file
  367.     JNC    Z1            ; Good open
  368.     MOV    DX,OFFSET VIRMEM    ; Point to filename
  369.     MOV    SI,OFFSET ERROR8    ; Point to 'open'
  370.     JMP    SHO_ERR            ; Show error, implied return
  371. ;
  372. Z1:    PUSH    DS            ; Save segment
  373.     MOV    DS,WORD PTR [CS_SAV]    ; Pick up code segment
  374.     MOV    BX,AX            ; File handle
  375. RD_ALL:    MOV    CX,0FFFFH        ; 64k -1
  376.     XOR    DX,DX            ; Offset 0
  377.     MOV    AX,3F00H        ; Read from file
  378.     INT    MS_DOS            ; Write the file
  379.     JC    RD_DON            ; Bad read
  380.     CMP    CX,AX            ; See if end of file
  381.     JNZ    RD_DON            ; No more bytes, all done
  382.     MOV    AX,DS            ; Pick up segment
  383.     ADD    AX,1000H        ; Next 64k block
  384.     MOV    DS,AX            ; Update segment
  385.     JMP    SHORT RD_ALL        ; Continue
  386. RD_DON:    POP    DS            ; Restore segment
  387.     JNC    Z2
  388.     MOV    DX,OFFSET VIRMEM    ; Point to filename
  389.     MOV    SI,OFFSET ERROR9    ; Point to 'read'
  390.     JMP    SHO_ERR            ; Show error, implied return
  391. ;
  392. Z2:    MOV    AX,3E00H        ; Close file handle
  393.     INT    MS_DOS
  394.     JNC    Z3            ; Good close
  395.     MOV    DX,OFFSET VIRMEM    ; Point to filename
  396.     MOV    SI,OFFSET ERROR7    ; Point to 'close'
  397.     JMP    SHO_ERR            ; Show error, implied return
  398. ;
  399. Z3:    MOV    DX,OFFSET VIRMEM    ; Point to code-data filename
  400.     MOV    AX,4100H        ; Delete the file
  401.     INT    MS_DOS
  402.     JNC    Z4            ; Good delete
  403.     MOV    DX,OFFSET VIRMEM    ; Point to filename
  404.     MOV    SI,OFFSET ERROR10    ; Point to 'delete'
  405.     JMP    SHO_ERR            ; Show error, implied return
  406. ;
  407. Z4:    RET
  408. RES_MEM    ENDP
  409. ;
  410. ;    Restore the DTA
  411. ;
  412. RES_DTA    PROC    NEAR
  413.     PUSH    DS            ; Save segment
  414.     LDS    DX,DWORD PTR [DTA]    ; Get old DTA address
  415.     MOV    AX,1A00H        ; Set DTA address
  416.     INT    MS_DOS
  417.     POP    DS            ; Restore segment
  418.     RET
  419. RES_DTA    ENDP
  420. ;
  421. ;    Save the DTA
  422. ;
  423. SAV_DTA    PROC    NEAR
  424.     PUSH    ES            ; Save segment
  425.     MOV    AX,2F00H        ; Get DTA address
  426.     INT    MS_DOS
  427.     MOV    WORD PTR [DTA_OFF],BX    ; Save current DTA address
  428.     MOV    WORD PTR [DTA_SEG],ES
  429.     POP    ES            ; Restore segment.
  430.     RET
  431. SAV_DTA    ENDP
  432. ;
  433. ;    Save the interrupt table.
  434. ;
  435. SAV_INT    PROC    NEAR
  436.     IN    AL,INT_CTL        ; Get interrupt controller mask
  437.     MOV    BYTE PTR [OLD_MSK],AL    ; Save the mask
  438.     OR    AL,00011100B        ; Reset IRQ3, IRQ4, Reserved
  439.     OUT    INT_CTL,AL        ; Set the mask
  440. ;
  441. ;    Save present interrupt vectors.
  442. ;
  443.     XOR    AX,AX            ; Get a zero
  444.     MOV    SI,AX            ; Offset zero
  445.     MOV    CX,KB            ; 1k to move
  446. ;
  447.     PUSH    DS
  448.     MOV    DS,AX            ; Point to interrupt table
  449.     MOV    DI,OFFSET TABLE        ; Point to our data-space
  450.     CLI
  451.     REP    MOVSB            ; Copy to our CS
  452.     STI
  453.     POP    DS            ; Restore segment
  454. ;
  455. ;    Set the default interrupt vectors.
  456. ;
  457.     XOR    AX,AX
  458.     MOV    DI,AX            ; Offset zero
  459.     MOV    SI,OFFSET DEFAULT    ; Where the interrupt data is
  460.     MOV    CX,KB            ; 1k to replace
  461. ;
  462.     PUSH    ES            ; Save the segment
  463.     MOV    ES,AX            ; Zero the segment
  464.     CLI
  465.     REP    MOVSB
  466.     STI
  467.     POP    ES            ; Restore segment
  468.     RET
  469. SAV_INT    ENDP
  470. ;
  471. ;    Restore the interrupt table
  472. ;
  473. RES_INT    PROC    NEAR
  474.     XOR    AX,AX
  475.     MOV    DI,AX            ; Offset zero
  476.     MOV    SI,OFFSET TABLE        ; Where the interrupt data is
  477.     MOV    CX,KB            ; 1k to replace
  478. ;
  479.     PUSH    ES            ; Save the segment
  480.     MOV    ES,AX            ; Zero the segment
  481.     CLI
  482.     REP    MOVSB
  483.     STI
  484.     POP    ES            ; Restore segment
  485. ;
  486.     MOV    AL,BYTE PTR [OLD_MSK]    ; Get old controller mask
  487.     OUT    INT_CTL,AL        ; Set interrupt controller mask
  488.     RET
  489. RES_INT    ENDP
  490. ;
  491. ;    Clear the screen, write message.
  492. ;
  493. CLR_SCR    PROC    NEAR
  494.     MOV    AL,BYTE PTR [SCR_MOD]    ; Get screen mode byte
  495.     MOV    AH,0            ; Set mode function
  496.     INT    VIDEO            ; That should clear the screen
  497.     MOV    SI,OFFSET LOGO2        ; Point to 'EXIT' message
  498.     CALL    SIGNON            ; Print to screen
  499.     RET
  500. CLR_SCR    ENDP
  501. ;
  502. ;    Save the default directory.
  503. ;
  504. SAV_DIR    PROC    NEAR
  505.     MOV    AH,47H            ; Get current directory function
  506.     XOR    DL,DL            ; Get default
  507.     MOV    SI,OFFSET CUR_DIR    ; Where to copy the string
  508.     INT    MS_DOS            ; Put it there
  509.     MOV    AH,19H            ; Get current disk
  510.     INT    MS_DOS            ; From DOS
  511.     ADD    AL,'A'            ; Drive bias
  512.     MOV    BYTE PTR [DRV],AL    ; Put in drive specifier
  513.     RET
  514. SAV_DIR    ENDP
  515. ;
  516. SAV_SCR    PROC    NEAR
  517.     MOV    AH,15            ; Return video state
  518.     INT    VIDEO            ; Video BIOS
  519.     MOV    BYTE PTR [SCR_MOD],AL    ; Current mode
  520.     MOV    BYTE PTR [SCR_COL],AH    ; Number of columns
  521.     MOV    BYTE PTR [SCR_PAG],BH    ; Current page
  522. ;
  523.     MOV    AH,3            ; Get cursor position
  524.     INT    VIDEO            ; Video BIOS
  525.     MOV    WORD PTR [CUR_POS],DX    ; Save cursor position
  526.     MOV    WORD PTR [CUR_TYP],CX    ; Save cursor type
  527. ;
  528.     MOV    DX,OFFSET SCREEN    ; Point to screen-data filename
  529.     MOV    AX,3C00H        ; Create file function
  530.     XOR    CH,CH            ; File attributes
  531.     MOV    CL,00000110B        ; Hidden/system
  532.     INT    MS_DOS            ; Create the file
  533.     JNC    Y1            ; Good create
  534.     MOV    DX,OFFSET SCREEN    ; Point to filename
  535.     MOV    SI,OFFSET ERROR5    ; Point to 'create'
  536.     JMP    SHO_ERR            ; Show error, implied return
  537. ;
  538. Y1:    MOV    BX,AX            ; File handle
  539.     MOV    CX,0FFFFH        ; 64k -1
  540.     XOR    DX,DX            ; Offset zero
  541.     MOV    AX,4000H        ; Write to file
  542.     PUSH    DS            ; Save segment
  543.     MOV    DS,WORD PTR [SCR_SEG]    ; Pick up screen segment
  544.     INT    MS_DOS            ; Write the file
  545.     POP    DS            ; Restore segment
  546.     JNC    Y2            ; Good write
  547.     MOV    DX,OFFSET SCREEN    ; Point to filename
  548.     MOV    SI,OFFSET ERROR6    ; Point to 'write'
  549.     JMP    SHO_ERR            ; Show error, implied return
  550. ;
  551. Y2:    MOV    AX,3E00H        ; Close file handle
  552.     INT    MS_DOS
  553.     JNC    Y3            ; Good close
  554.     MOV    DX,OFFSET SCREEN    ; Point to filename
  555.     MOV    SI,OFFSET ERROR7    ; Point to 'close'
  556.     JMP    SHO_ERR            ; Show error, implied return
  557. Y3:    RET
  558. SAV_SCR    ENDP
  559. ;
  560. ;    Restore the default directory.
  561. ;
  562. RES_DIR    PROC    NEAR
  563.     MOV    AH,0EH            ; Change drive function
  564.     MOV    DL,BYTE PTR [DRV]    ; Get saved drive letter
  565.     SUB    DL,'A'            ; Remove bias
  566.     INT    MS_DOS            ; Go do it
  567. ;
  568.     MOV    AH,3BH            ; Change directory function
  569.     MOV    DX,OFFSET DRV        ; Full drive/path name
  570.     INT    MS_DOS
  571.     RET
  572. RES_DIR    ENDP
  573. ;
  574. RES_SCR    PROC    NEAR
  575.     MOV    DX,OFFSET SCREEN    ; Point to screen-data filename
  576.     MOV    AX,3D00H        ; Open file for reading
  577.     XOR    CX,CX            ; File attributes
  578.     INT    MS_DOS            ; Open the file
  579.     JNC    X1            ; Good open
  580.     MOV    DX,OFFSET SCREEN    ; Filename
  581.     MOV    SI,OFFSET ERROR8    ; Point to 'open'
  582.     JMP    SHO_ERR            ; Show the error, Implied return
  583. ;
  584. X1:    MOV    BX,AX            ; File handle
  585.     MOV    CX,0FFFFH        ; 64k -1
  586.     XOR    DX,DX            ; Offset zero
  587.     MOV    AX,3F00H        ; Read from file
  588.     PUSH    DS            ; Save segment
  589.     MOV    DS,WORD PTR [SCR_SEG]    ; Pick up screen segment
  590.     INT    MS_DOS            ; Write the file
  591.     POP    DS            ; Restore segment
  592.     JNC    X2            ; Good read
  593.     MOV    DX,OFFSET SCREEN    ; Filename
  594.     MOV    SI,OFFSET ERROR9    ; Point to 'read'
  595.     JMP    SHO_ERR            ; Implied return
  596. ;
  597. X2:    MOV    AX,3E00H        ; Close file handle
  598.     INT    MS_DOS
  599.     JNC    X3            ; Good close
  600.     MOV    DX,OFFSET SCREEN    ; Filename
  601.     MOV    SI,OFFSET ERROR7    ; Point to 'close'
  602.     JMP    SHO_ERR            ; Implied return
  603. ;
  604. X3:    MOV    DX,OFFSET SCREEN    ; Point to screen-data filename
  605.     MOV    AX,4100H        ; Delete the file
  606.     INT    MS_DOS
  607.     JNC    X4            ; Good delete
  608.     MOV    DX,OFFSET SCREEN    ; Filename
  609.     MOV    SI,OFFSET ERROR10    ; Point to 'delete'
  610.     JMP    SHO_ERR            ; Implied return
  611. ;
  612. X4:    MOV    AH,5            ; Select active page
  613.     MOV    AL,BYTE PTR [SCR_PAG]    ; Get previous screen page
  614.     INT    VIDEO            ; Video BIOS
  615. ;
  616.     MOV    AH,1            ; Set cursor type
  617.     MOV    CX,WORD PTR [CUR_TYP]    ; Get previous cursor type
  618.     INT    VIDEO            ; Video BIOS
  619. ;
  620.     MOV    AH,2            ; Set cursor position
  621.     MOV    BH,BYTE PTR [SCR_PAG]    ; Page number.
  622.     MOV    DX,WORD PTR [CUR_POS]    ; Get saved cursor position
  623.     INT    VIDEO            ; Video ROM BIOS
  624.     RET
  625. RES_SCR    ENDP
  626. ;
  627. PROMPT    PROC    NEAR
  628.     LODSB                ; Get memory byte
  629.     TEST    AL,AL            ; Check for a null
  630.     JZ    PEXIT            ; All done
  631.     MOV    BX,7            ; Page zero/normal attribute
  632.     MOV    AH,14            ; Dumb terminal mode
  633.     INT    VIDEO            ; Video BIOS
  634.     JMP    SHORT PROMPT        ; Continue
  635. PEXIT:    RET
  636. PROMPT    ENDP
  637. ;
  638. ;    Upon entry, DX points to filename. Si points to string for open/close
  639. ;    etc. The error message is printed to the screen.
  640. ;
  641. SHO_ERR    PROC    NEAR
  642.     PUSH    SI
  643.     MOV    SI,OFFSET ERROR4    ; Point to "Can't"
  644.     CALL    PROMPT            ; Write to screen
  645.     POP    SI            ; Point to create/write/read, etc
  646.     CALL    PROMPT            ; Write to screen
  647.     MOV    SI,OFFSET ERROR11    ; Point to "file"
  648.     CALL    PROMPT            ; Write to screen
  649.     MOV    SI,DX            ; Get file name
  650.     JMP    PROMPT            ; Write to screen, implied return.
  651. SHO_ERR    ENDP
  652. ;
  653. ;    Save UART parameters.
  654. ;
  655. SAV_UAR    PROC    NEAR
  656.     MOV    DI,OFFSET UAR_PAR    ; Point to Uart parameter table
  657.     MOV    DX,ADDR1        ; Pick up address of UART
  658.     CALL    GET_PAR            ; Get UART parameters
  659.     MOV    DX,ADDR2        ; Pick up address of UART
  660.     CALL    GET_PAR            ; Get UART parameters
  661.     MOV    DX,ADDR3        ; Pick up address of UART
  662.     CALL    GET_PAR            ; Get UART parameters
  663.     MOV    DX,ADDR4        ; Pick up address of UART
  664.     CALL    GET_PAR            ; Get UART parameters
  665.     RET
  666. SAV_UAR    ENDP
  667. ;
  668. ;    Get UART parameters. UART base-port is in DX. Storage for
  669. ;    parameters is pointed to by DI. After saving parameters, disable
  670. ;    any interrupt enable bits.
  671. ;
  672. GET_PAR    PROC    NEAR
  673.     ADD    DX,3            ; Offset to line control register
  674.     IN    AL,DX            ; Get line control bits
  675.     MOV    BL,AL            ; Save the bits
  676.     STOSB                ; Save in memory
  677.     OR    AL,10000000B        ; Divisor access bit
  678.     OUT    DX,AL            ; Set divisor access bit
  679.     SUB    DX,3            ; Back to base port
  680.     IN    AX,DX            ; Get divisor
  681.     STOSW                ; Save in memory
  682.     ADD    DX,3            ; Offset to line control register
  683.     MOV    AL,BL            ; Get saved line-control bits
  684.     OUT    DX,AL            ; Reset divisor latch
  685.     SUB    DX,2            ; Set to interrupt control register
  686.     IN    AL,DX            ; Get the control bits
  687.     STOSB                ; Save in memory
  688.     XOR    AL,AL            ; Turn off interrupt control
  689.     OUT    DX,AL            ; Reset the bits
  690.     ADD    DX,3            ; Offset to modem control
  691.     IN    AL,DX            ; Save modem control bits
  692.     STOSB                ; Save in memory
  693.     MOV    CX,6            ; Registers to read
  694.     SUB    DX,4            ; Back to base port
  695. RDALL1:    IN    AL,DX            ; Read port
  696.     INC    DX            ; Ready next
  697.     LOOP    RDALL1            ; Read all ports
  698.     MOV    AL,NOS_EOI        ; Non-specific end-of-interrupt
  699.     OUT    INT_RES,AL        ; Reset hardware controller.
  700.     RET
  701. GET_PAR    ENDP
  702. ;
  703. ;    Restore UART parameters.
  704. ;
  705. RES_UAR    PROC    NEAR
  706.     MOV    SI,OFFSET UAR_PAR    ; Point to Uart parameter table
  707.     MOV    DX,ADDR1        ; Pick up address of UART
  708.     CALL    SET_PAR            ; Set UART parameters
  709.     MOV    DX,ADDR2        ; Pick up address of UART
  710.     CALL    SET_PAR            ; Set UART parameters
  711.     MOV    DX,ADDR3        ; Pick up address of UART
  712.     CALL    SET_PAR            ; Set UART parameters
  713.     MOV    DX,ADDR4        ; Pick up address of UART
  714.     CALL    SET_PAR            ; Set UART parameters
  715.     RET
  716. RES_UAR    ENDP
  717. ;
  718. ;    Set UART parameters. UART base-port is in DX. Storage for
  719. ;    parameters is pointed to by SI.
  720. ;
  721. SET_PAR    PROC    NEAR
  722.     ADD    DX,3            ; Line control register
  723.     IN    AL,DX            ; Get present control-bits
  724.     OR    AL,10000000B        ; Set divisor latch access bit
  725.     OUT    DX,AL            ; To line-control register
  726.     LODSB                ; Get old line-control bits
  727.     MOV    BL,AL            ; Save for now
  728.     SUB    DX,3            ; Back to the base register
  729.     LODSW                ; Get saved divisor
  730.     OUT    DX,AX            ; Out the adjacent ports
  731.     ADD    DX,3            ; Back to line-control
  732.     MOV    AL,BL            ; Get saved control-bits
  733.     AND    AL,01111111B        ; Verify DLAB is reset
  734.     OUT    DX,AL            ; Restore line-control
  735.     SUB    DX,2            ; Set to interrupt control register
  736.     LODSB                ; Get old bits
  737.     OUT    DX,AL            ; Restore
  738.     ADD    DX,3            ; Offset to modem control
  739.     LODSB                ; Get old bits
  740.     OUT    DX,AL            ; Restore
  741.     MOV    CX,6            ; Registers to read
  742.     SUB    DX,4            ; Back to base port
  743. RDALL2:    IN    AL,DX            ; Read port
  744.     INC    DX            ; Ready next
  745.     LOOP    RDALL2            ; Read all ports
  746.     MOV    AL,NOS_EOI        ; Non-specific end-of-interrupt
  747.     OUT    INT_RES,AL        ; Reset hardware controller.
  748.     RET
  749. SET_PAR    ENDP
  750. ;
  751. ERROR1    DB    CR,LF,'Can''t load the command processor! <CR> ',0
  752. ERROR2    DB    CR,LF,'Can''t release memory! <CR> ',0
  753. ERROR4    DB    CR,LF,'Can''t ',0
  754. ERROR5    DB    'create',0
  755. ERROR6    DB    'write',0
  756. ERROR7    DB    'close',0
  757. ERROR8    DB    'open',0
  758. ERROR9    DB    'read',0
  759. ERROR10    DB    'delete',0
  760. ERROR11    DB    ' file ',0
  761. OLD_MSK    DB    ?            ; Old interrupt mask.
  762. SPAWN    DB    0            ; Flag for spawned sub-process
  763. EFLAG    DB    0            ; Entry flag
  764. SCR_MOD    DB    ?            ; Screen mode
  765. SCR_PAG    DB    ?            ; Screen page
  766. SCR_COL    DB    ?            ; Columns on screen
  767. CS_SAV    DW    ?            ; Interrupted program's CS
  768. SP_SAV    DW    ?            ; Interrupted program's SP
  769. SS_SAV    DW    ?            ; Interrupted program's SS
  770. SP_NEW    DW    ?            ; Save SP for EXEC call
  771. SCR_SEG    DW    0B000H            ; Segment of screen regen buffer
  772. CUR_TYP    DW    ?            ; Cursor type
  773. CUR_POS    DW    ?            ; Cursor position
  774. DTA    LABEL    DWORD            ; Data transfer area
  775. DTA_OFF    DW    ?            ; Offset
  776. DTA_SEG    DW    ?            ; Segment
  777. OLD_KBD    LABEL    DWORD            ; Old keyboard vector
  778. KBD_OFF    DW    ?            ; Offset
  779. KBD_SEG    DW    ?            ; Segment
  780. OLD_CLK    LABEL    DWORD            ; Old clock vector
  781. CLK_OFF    DW    ?            ; Offset
  782. CLK_SEG    DW    ?            ; Segment
  783. ;
  784. COMSPEC    DB    'COMSPEC='        ; Environment search string
  785. COMLEN    EQU    $ - COMSPEC        ; It's length
  786. COMMAND    DB    65 DUP (0)        ; To copy command processor name
  787. DRV    DB    '?:\'            ; Current directory drive and root.
  788. CUR_DIR    DB    65 DUP (0)        ; Rest of the current directory string
  789. SCREEN    DB    '\SCREEN.$$$',0        ; Screen data file.
  790. VIRMEM    DB    '\VIRTUAL.MEM',0    ; Virtual memory file
  791. ;
  792. ;    Parameter block for EXEC function call
  793. ;
  794. BLOCK    DW    0            ; Use current environment
  795.     DW    80H            ; Offset of command line
  796. CS0    DW    ?            ; Segment of command line
  797.     DW    5CH            ; Offset of FCB #1
  798. CS1    DW    ?            ; Segment of FCB #1
  799.     DW    6CH            ; Offset of FCB #2
  800. CS2    DW    ?            ; Segment of FCB #2
  801. ;
  802. UAR_PAR    LABEL    BYTE            ; Where the UART parameters are kept.
  803.     IN8250    <>
  804.     IN8250    <>
  805.     IN8250    <>
  806.     IN8250    <>
  807. ;
  808. LOGO2    DB    28 DUP (' ')
  809. STRTL2    DB    (28 - ( @VERS + 4 ) ) / 2 DUP (' ')
  810.     DB    'JDOS '
  811.     VERS <>
  812. ENDL2    EQU    $ - STRTL2
  813.     DB    28 - ENDL2 DUP (' ')
  814.     DB    ' Resident Command Processor '
  815.     DB    '           ACTIVE           '
  816.     DB    '  Type EXIT ─┘ to return   '
  817.     DB    28 DUP (' ')
  818. ;
  819. ;    Put on a paragraph boundary.
  820. ;
  821.     ORG    (( ($-START) + 16) AND 1111111111110000B)
  822.     DB    32 DUP ('STACK   ')
  823. ;
  824. STKTOP    LABEL    WORD
  825. DEFAULT    LABEL    BYTE            ; For our default interrupt table
  826.     ORG    $ + KB
  827. BUFFER    LABEL    BYTE            ; Misc buffer for environment parse
  828. TABLE    LABEL    BYTE            ; Saved interrupt table during spawn
  829.     ORG    $ + KB
  830. TOP    EQU    $
  831. ;
  832. INIT    PROC    NEAR
  833.     MOV    AX,3000H        ; Get DOS version number
  834.     INT    MS_DOS            ; From DOS
  835.     CMP    AL,3            ; Need verion 3+
  836.     JNC    VERSOK            ; Its okay
  837.     MOV    SI,OFFSET ERROR12    ; Point to version error
  838. EXITF:    CALL    PROMPT            ; Write to screen, exit fatal
  839.     MOV    AX,4C01H        ; Exit, ERRORLEVEL 1
  840.     INT    MS_DOS            ; To DOS
  841. ;
  842. VERSOK:    INT    11H            ; Equipment check
  843.     AND    AL,00110000B        ; Mask everything but video info
  844.     CMP    AL,00100000B        ; See if color
  845.     JNZ    DEFULT            ; Default is mono
  846.     MOV    WORD PTR [SCR_SEG],0B800H ; Color screen segment
  847.  
  848. DEFULT:    CALL    CHK_SEG            ; See if its writable memory
  849.     JZ    SCR_OK            ; Found writable memory
  850.     MOV    WORD PTR [SCR_SEG],(0B000H - 100H); Start at lowest segment
  851. SERCH0:    ADD    WORD PTR [SCR_SEG],00100H ; Incr screen segment
  852.     CMP    WORD PTR [SCR_SEG],0E000H ; Upper limit of search
  853.     JC    SERCH1            ; Not outside limits
  854.     MOV    SI,OFFSET ERROR13    ; Point to "can't find screen"
  855.     JMP    SHORT EXITF        ; Write to screen and exit fatal
  856. SERCH1:    CALL    CHK_SEG            ; Check for writable memory
  857.     JNZ    SERCH0            ; Keep looking
  858. ;
  859. SCR_OK:    CALL    GET_CMD            ; Get command processor
  860.     JNC    INIT2            ; Found
  861.     MOV    SI,OFFSET ERROR1    ; Point to error message
  862.     JMP    SHORT EXITF        ; Write to screen and exit fatal
  863. ;
  864. INIT2:    MOV    WORD PTR [CS0],CS    ; Fix up parameter block
  865.     MOV    WORD PTR [CS1],CS
  866.     MOV    WORD PTR [CS2],CS
  867.     MOV    AH,35H            ; Get vector function
  868.     MOV    AL,TICK1        ; Clock tick
  869.     INT    MS_DOS            ; Go get it
  870.     MOV    WORD PTR [CLK_OFF],BX    ; Save the vector
  871.     MOV    WORD PTR [CLK_SEG],ES
  872. ;
  873.     MOV    AH,35H            ; Get vector function
  874.     MOV    AL,16H            ; Kbd interrupt
  875.     INT    MS_DOS            ; Get the vector
  876.     MOV    WORD PTR [KBD_OFF],BX    ; Save the vector
  877.     MOV    WORD PTR [KBD_SEG],ES
  878. ;
  879.     MOV    DI,BX            ; ES:DI = old vector
  880.     MOV    SI,OFFSET LCL_KBD    ; DS:SI = new vector
  881.     MOV    CX,10H            ; Check 16 bytes
  882.     REPZ    CMPSB
  883.     PUSH    CS
  884.     POP    ES            ; restore segment
  885.     JNZ    NEW            ; Never installed before
  886. ;
  887.     MOV    SI,OFFSET ERROR3    ; Point to error message
  888.     CALL    PROMPT            ; Display to console
  889.     MOV    AX,4C00H        ; Exit to DOS ERRORLEVEL 0
  890.     INT    MS_DOS
  891. ;
  892. NEW:    MOV    AH,25H            ; Set vector function
  893.     MOV    AL,TICK1        ; Vector to set
  894.     MOV    DX,OFFSET ENTRY        ; Vector to patch
  895.     INT    MS_DOS            ; Patch the vector
  896. ;
  897.     MOV    AH,25H            ; Set vector function
  898.     MOV    AL,16H            ; Vector to set
  899.     MOV    DX,OFFSET LCL_KBD    ; Vector to patch
  900.     INT    MS_DOS            ; Patch the vector
  901. ;
  902.     PUSH    ES            ; Save segment
  903.     MOV    ES,WORD PTR DS:[ENVIR]    ; Get environment segment
  904.     XOR    BX,BX            ; Free it all up
  905.     MOV    AX,4A00H        ; Modify memory
  906.     INT    MS_DOS            ; Call DOS
  907.     POP    ES            ; Restore segment
  908. ;
  909. ;    Save our default interrupt table.
  910. ;
  911.     XOR    AX,AX            ; Get a zero
  912.     MOV    SI,AX            ; Offset zero
  913.     MOV    CX,KB            ; 1k to move
  914. ;
  915.     PUSH    DS
  916.     MOV    DS,AX            ; Point to interrupt table
  917.     MOV    DI,OFFSET DEFAULT    ; Point to our data-space
  918.     CLI
  919.     REP    MOVSB            ; Copy to our CS
  920.     STI
  921.     POP    DS            ; Restore segment
  922. ;
  923.     MOV    SI,OFFSET LOGO1        ; Point to 'installed' logo
  924.     CALL    SIGNON            ; Signon the message.
  925.     MOV    AX,3100H        ; Keep process
  926.     MOV    DX,OFFSET TOP        ; Last location to keep
  927.     ADD    DX,MEM            ; Memory we need
  928.     SHR    DX,1            ; Div/2
  929.     SHR    DX,1            ; Div/4
  930.     SHR    DX,1            ; Div/8
  931.     SHR    DX,1            ; Div/16
  932.     INT    MS_DOS            ; Exit to DOS
  933.     JMP    $            ; For fatal error abort
  934. INIT    ENDP
  935. ;
  936. ;    Check screen segment for writable memory.
  937. ;
  938. CHK_SEG    PROC    NEAR
  939.     PUSH    DS            ; Save segment
  940.     MOV    DS,WORD PTR [SCR_SEG]    ; Get screen segment
  941.     MOV    AX,WORD PTR DS:[0]    ; Get word at address zero
  942.     MOV    BX,AX            ; Save memory word
  943.     NOT    AX            ; Invert
  944.     NOT    WORD PTR DS:[0]        ; Invert memory word
  945.     CMP    AX,WORD PTR DS:[0]    ; See if it went
  946.     MOV    WORD PTR DS:[0],BX    ; Put original word back
  947.     POP    DS            ; Restore segment
  948.     RET
  949. CHK_SEG    ENDP
  950. ;
  951. ;    Get boot command interpreter. Find out where COMMAND.COM is.
  952. ;
  953. GET_CMD    PROC    NEAR
  954.     XOR    SI,SI            ; Offset zero
  955.     MOV    DI,OFFSET BUFFER    ; Where to copy the string
  956.     MOV    AX,WORD PTR DS:[ENVIR]    ; Get environment segment
  957.     PUSH    DS            ; Save segment
  958.     MOV    DS,AX            ; Set environment segment
  959. GET0:    LODSB                ; Get environment byte
  960.     TEST    AL,AL            ; Check for a null
  961.     JNZ    GET1            ; Not a null
  962.     CMP    BYTE PTR [SI],0        ; Next on a null too?
  963.     JZ    GET3            ; Yes, all done
  964. GET1:    CMP    AL,'z'            ; Check limits
  965.     JA    GET2            ; Not lower case
  966.     CMP    AL,'a'            ; Check lower limit
  967.     JB    GET2            ; Not lower case
  968.     AND    AL,95            ; Reset lower-case bits
  969. GET2:    STOSB                ; Save byte
  970.     JMP    SHORT GET0        ; Continue until the double-null
  971. GET3:    STOSB                ; Final null
  972.     POP    DS            ; Restore segment
  973. ;
  974.     MOV    CX,DI            ; Get last pointer
  975.     SUB    CX,OFFSET BUFFER    ; CX= length of environment strings
  976.     MOV    BX,OFFSET COMSPEC    ; Substring to find
  977.     MOV    DX,COMLEN        ; Length of the substring
  978.     MOV    SI,OFFSET BUFFER    ; Where the string should be found.
  979.     CALL    COMPARE            ; Scan the string
  980.     JNZ    GET10            ; Not found
  981.     MOV    DI,OFFSET COMMAND    ; Where to copy COMMAND.COM
  982.     MOV    BX,SI            ; Save location
  983. GET4:    LODSB                ; Get byte to transfer
  984.     TEST    AL,AL            ; Check for terminator
  985.     JZ    GET5            ; String is transferred
  986.     STOSB                ; Copy byte
  987.     JMP    SHORT GET4        ; Continue
  988. GET5:    RET                ; No errors
  989. GET10:    STC                ; Show error
  990.     RET
  991. GET_CMD    ENDP
  992. ;
  993. ;    Compares the substring addressed by DS:BX to the string addressed
  994. ;    by DS:SI. DX contains the substring length. CX contains the string
  995. ;    length. Returns ZF=TRUE if the string is found. SI points to one
  996. ;    character after the string if its found.
  997. ;
  998. COMPARE    PROC    NEAR
  999.     PUSH    CX            ; Save string length
  1000. COMP0:    PUSH    CX            ; Save original string length
  1001.     MOV    CX,DX            ; Get substring length
  1002.     MOV    DI,BX            ; Get substring location
  1003.     REPZ    CMPSB            ; Compare string/substring
  1004.     POP    CX            ; Restore string length
  1005.     JZ    FOUND            ; String was found
  1006.     LOOP    COMP0            ; Not found, continue
  1007.     INC    CX            ; Make NZ
  1008. FOUND:    POP    CX            ; Restore string length
  1009.     RET
  1010. COMPARE    ENDP
  1011. ;
  1012. ERROR3    DB    CR,LF,'JDOS is already installed!',CR,LF,0
  1013. ERROR12    DB    CR,LF,'Need DOS version 3.0 or higher to execute!',CR,LF,0
  1014. ERROR13    DB    CR,LF,'Can''t find writable screen memory!',CR,LF,0
  1015. LOGO1    DB    28 DUP (' ')
  1016. STRTL    DB    (28 - ( @VERS + 4 ) ) / 2 DUP (' ')
  1017.     DB    'JDOS '
  1018.     VERS <>
  1019. ENDL    EQU    $ - STRTL
  1020.     DB    28 - ENDL DUP (' ')
  1021.     DB    ' Resident Command Processor '
  1022.     DB    '                            '
  1023.     DB    '    Use ^\ to activate.     '
  1024.     DB    28 DUP (' ')
  1025. PSEG    ENDS
  1026.     END    MAIN
  1027.